# 8. vue编译原理

# 编译原理

模板结构字符串 -> 用正则和indexof('<')将字符串转为AST语法树对象 -> 转为render函数 -> 返回虚拟Dom

模板结构:

parseHTML(`<div id="container"><p>hello<span>{{msg}}</span></p></div>`)
1

AST(abstract syntax tree)意为抽象语法树,其实就是树形数据结构的表现形式,用来描述字符串语法。

{
  tag:'div',
  type:1,
  children: [
    {
      tag:'p',
      type: 1,
      attrs: [],
      children: [Array],
      parent: [Circular]
    }
  ],
  attrs: [{name:'id',value:'container'}],
  parent: null
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

render函数返回vnode:

function render() {
  with(this) {//with用来绑定作用域
    return _c('div', {
      attrs: {
        "id": "container"
      }
    }, [_c('p', [_v("hello"), _c('span', [_v(_s(msg))])])])
  }
}

//相当于:
render: function (createElement) {
  return createElement('div', {
     attrs: {
        id: 'app'
      },
  }, this.message)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

AST和虚拟节点vnode有什么关系?
它们结构很相似,AST其实算得上是vnode的前身,AST经过一系列的指令解析、数据渲染就会变成vnode!这边的AST其实只是简单的html解析。vnode是用来描述DOM结构的。

# 为何需要Virtual DOM?

  1. 具备跨平台的优势

由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。

  1. 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。

因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)

  1. 提升渲染性能

Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。

# v-if

const templateCompiler = require('vue-template-compoler');

templateCompiler.compile(`<div v-if="true"><span v-for="i in 3">hello</span></div>`)


//编译为render函数
function render() {
  with(this) {
    return (true) ? _c('div', _l((3), function (i) {
      return _c('span', [_v("hello")])
    }), 0) : _e()
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

由上面代码可以看出将v-if实际编译后变为三目运算,v-for编译后变为执行三次函数。

const templateCompiler = require('vue-template-compoler');

templateCompiler.compile(`<div v-if="true" v-for="i in 3">hello</div>`)


//编译为render函数
function render() {
  with(this) {
    return  _l((3), function (i) {
      return (true) ? _c('div', [_v("hello")])
    }), 0) : _e()
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

这也是为啥v-forv-if在同一标签上v-for优先级高,但最好不要写在同一个标签上,因为写在同一个标签上会影响编译和渲染性能。

# v-show

const templateCompiler = require('vue-template-compoler');

templateCompiler.compile(`<div v-show="true">hello</div>`)


//编译为render函数
function render() {
  with(this) {
    return _c('div', {
      directives: [{
        name: "show",
        rawName: "v-show",
        value: (true),
        expression: "true"
      }]
    }, [_v("hello")])
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

v-show编译后会被转为指令,通过指令来控制el.style.display的显示隐藏。